home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
et
/
et3_0-a1.lha
/
et3
/
src
/
StaticTView.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-08-24
|
20KB
|
838 lines
#ifdef __GNUG__
#pragma implementation
#endif
#include "StaticTView.h"
#include "Class.h"
#include "String.h"
#include "View.h"
#include "Window.h"
#include "PrintManager.h"
#include "TextFormatter.h"
#include "TextPainter.h"
#include "TextStyles.h"
#include "CheapText.h"
#include "Invariant.h"
#include "Document.h"
#include "Math.h"
Rectangle gFitRect(cFit, cFit);
void StaticTextView::SwapRange(int &a, int &b)
{
int tmp;
tmp= a; a= b; b= tmp;
}
inline LineMark *MarkAt(ObjArray *oa, int at)
{
return (LineMark*)(*oa)[at];
}
void StaticTextView::Dump()
{
for(int i= 0; i < nLines; i++) {
if(MarkAtLine(i) == 0)
fprintf(stderr, "Mark is NULL at %d\n!!!", i);
else
fprintf(stderr, "%d. %d-%d\n", i, StartLine(i), EndLine(i));
}
fprintf(stderr, "nLines= %d\n", nLines);
}
void StaticTextView::ClassInvariant()
{
if (GetAssertLevel() > 5)
return;
if(nLines > lines->Size())
fprintf(stderr, "!!!StaticTextView Invariant: nLines > lines->Size()!!!\n");
for(int i= 1; i < nLines; i++) {
if(MarkAtLine(i) == 0) {
fprintf(stderr, "!!!StaticTextView Invariant: Mark is NULL at %d!!!\n", i);
Dump();
}
else if(StartLine(i) == EndLine(i)) {
fprintf(stderr, "!!!StaticTextView Invariant: Empty line at %d!!!\n", i);
Dump();
}
else if(StartLine(i) != EndLine(i-1)) {
fprintf(stderr, "!!!StaticTextView Invariant: Discontinous lines %d %d %d!!!\n", i, StartLine(i),
EndLine(i-1));
Dump();
}
}
}
static TextPainter *sPainter;
static TextFormatter *sFormatter;
static CheapText *sText;
ONEXIT(StaticTextView)
{
SafeDelete(sPainter);
SafeDelete(sFormatter);
SafeDelete(sText);
}
//---- static methods ---------------------------------------------------------
Metric StaticTextView::MeasureText(char *text, Font* fd, int minwidth, int l)
{
if (l <= 1) {
if (minwidth > 0 || text == 0)
return Metric(minwidth, fd->Spacing(), fd->Ascender());
return Metric(fd->Width((byte*)text), fd->Spacing(), fd->Ascender());
}
return Metric(minwidth, l*fd->Spacing(), fd->Ascender());
}
void StaticTextView::DrawBoxedText(Rectangle r, char *txt,
Font *fp, Ink *ink, bool wrap)
{
Point p(r.origin);
p.y+= fp->Ascender();
if (!wrap) {
Metric m(fp->Width((byte*)txt), fp->Spacing(), fp->Ascender());
if (m.extent.x > r.extent.x || m.extent.y > r.extent.y) {
GrState gs;
GrClipFurther(r);
GrShowString(fp, ink, p, (byte*)txt);
} else
GrShowString(fp, ink, p, (byte*)txt);
return;
}
if (sPainter == 0) {
sPainter= new TextPainter;
sFormatter= new FoldingFormatter;
sText= new CheapText;
}
sText->ReplaceWithStr((byte*)txt);
sText->SetFont(fp);
int nLines= sFormatter->Format(sText, sPainter, r.Width(), 0, sText->Size());
ObjArray *lines= sFormatter->GetLines();
GrState gs;
GrClipFurther(r);
Ink *oldtextink= GrGetTextInk();
GrSetTextInk(ink);
register int i, lh, bh;
Rectangle lr(p.x, p.y-fp->Ascender(), p.x, MarkAt(lines, 0)->Height());
bh= MarkAt(lines, 0)->Base();
for (i= 0; i < nLines; i++) {
int start= MarkAt(lines, i)->Pos(), end= MarkAt(lines, i)->End();
sPainter->Draw(sText, p, start, end, lr, r, FALSE);
p.y+= (lh= MarkAt(lines, i)->Height())-bh;
if (i < nLines-1)
p.y+= (bh= MarkAt(lines, i+1)->Base());
lr.origin.y+= lh;
}
GrSetTextInk(oldtextink);
}
//----- StaticTextView Methods -------------------------------------------------
NewMetaImpl(StaticTextView, View, (TP(text), TP(pager), TP(painter), TP(formatter),
TE(align), TB(wrap), TB(drawViewBorder), TB(horExtend), T(border),
T(nLines), TP(lines), TP(marks)));
StaticTextView::StaticTextView(
EvtHandler *eh, Rectangle r, Text *t,
bool w, TextViewFlags f, Point b,
TViewAlign ta, int id
) : View(eh, r, id)
{
Init(r, t, w, f, b, ta);
}
void StaticTextView::Init(
Rectangle r, Text* t, bool w, TextViewFlags f, Point b, TViewAlign ta
)
{
lines= new ObjArray(32);
marks= new MarkList(FALSE, lines);
contentRect= r;
text= t;
align= ta;
wrap= w;
drawViewBorder= FALSE;
border= b;
cline= 0;
cYPosition= 0;
horExtend= (contentRect.extent.x == cFit);
if (horExtend)
wrap= FALSE;
if (!wrap)
formatter= new SimpleFormatter();
else
formatter= new FoldingFormatter();
painter= new TextPainter();
RepairAll();
if ((f & eTextViewInvis) == eTextViewInvis)
SetFlag(eTextViewInvis);
if ((f & eTextViewReadOnly) == eTextViewReadOnly)
SetFlag(eTextViewReadOnly);
if ((f & eTextWantFindFocus) == eTextWantFindFocus)
SetFlag(eTextWantFindFocus);
if ((f & eTextViewNoBatch) == eTextViewNoBatch)
SetFlag(eTextViewNoBatch);
if ((f & eTextViewNoClip) == eTextViewNoClip)
SetFlag(eTextViewNoClip);
}
StaticTextView::~StaticTextView()
{
if (lines)
lines->FreeAll();
SafeDelete(marks);
SafeDelete(formatter);
SafeDelete(painter);
SafeDelete(pager);
}
void StaticTextView::Draw(Rectangle r)
{
register int i, lh, bh;
int startAt= PointToLine(r.origin-GetInnerOrigin());
Ink *oldtextink= 0;
if (startAt >= nLines)
return;
GrState gs;
if (!TestFlag(eTextViewNoClip))
GrClipFurther(Rectangle(GetInnerOrigin(), GetInnerExtent()));
Point p= LineToPoint(startAt,TRUE) + GetInnerOrigin();
Rectangle lineRect(
GetInnerOrigin().x, p.y-BaseHeight(startAt),
GetInnerExtent().x, LineHeight(startAt)
);
if (!Enabled()) {
oldtextink= GrGetTextInk();
GrSetTextInk(ePatGrey50);
}
bh= BaseHeight(startAt);
for (i= startAt; ; i++) {
if (! r.Intersects(lineRect))
break;
DrawLine(p, i, lineRect, r);
if (i >= nLines-1)
break;
p.y+= (lh= LineHeight(i))-bh;
p.y+= (bh= BaseHeight(i+1));
lineRect.origin.y+= lh;
}
if (drawViewBorder) {
GrSetPenInk(ePatGrey50);
GrSetPenSize(2);
GrStrokeRect (Rectangle (contentRect.origin,contentRect.extent));
}
if (! Enabled())
GrSetTextInk(oldtextink);
}
void StaticTextView::Repair(int from, int to, bool redraw)
{
int prevBreak, nextBreak;
int prevLine, nextLine;
ResetFlag(eTextViewModified);
prevBreak= text->FindLastBreak(from);
if(prevBreak < 0)
prevLine= -1;
else
prevLine= CharToLine(prevBreak);
prevLine= Math::Min(nLines-1, prevLine+1);
nextBreak= text->FindNextBreak(to);
if(nextBreak >= text->Size())
nextLine= nLines-1;
else
nextLine= CharToLine(nextBreak);
int n= formatter->Format(
text, painter, GetInnerExtent().x,
prevBreak+1, nextBreak+1
);
ObjArray *newLines= formatter->GetLines();
int extentDiff= UpdateView(prevLine, nextLine, n, newLines, redraw);
UpdateComposition(prevLine, nextLine, n, newLines);
UpdateExtent(extentDiff);
if (pager)
pager->Repaginate(prevLine);
// ClassInvariant();
}
Rectangle StaticTextView::NextPageBreak(int pn, Rectangle r)
{
if (pager == 0)
pager= new TextPager();
return pager->NextPageBreak(pn, r, this);
}
int StaticTextView::UpdateView(
int prev, int next, int n, ObjArray *newLines, bool redraw
)
{
int i, j;
int invalid, valid;
int newHeight= 0, oldHeight= 0;
for(i= 0; i < n; i++)
newHeight+= MarkAt(newLines, i)->Height();
for(i= prev; i <= next; i++)
oldHeight+= MarkAtLine(i)->Height();
if(redraw) {
invalid= prev;
valid= cMaxInt;
} else {
invalid= prev;
for(i= prev; i < prev+n && i <= next; i++) {
if(MarkAt(newLines, i-prev)->IsDifferent(MarkAtLine(i)))
break;
}
invalid= i;
valid= nLines;
if (oldHeight == newHeight) {
for(j= prev+n-1; j > invalid && j < nLines; j--) {
if(!MarkAt(newLines, j-prev)->IsDifferent(MarkAtLine(j)))
valid= j;
else
break;
}
if(valid == nLines)
valid= Math::Min(nLines, prev+n);
InvalidateRange(invalid, Math::Max(0, valid-1)); //
}
else
InvalidateRange(invalid, Math::Max(0, valid-1));
}
InvalidateRange(invalid, Math::Max(0, valid-1));
//---- update line cache
LineToPoint(Math::Max(0, invalid-1));
return newHeight-oldHeight;
}
void StaticTextView::UpdateComposition(
int prev, int next, int n, ObjArray *newLines
)
{
int diff= n-(next-prev+1);
register int i;
if(lines->Size() < nLines+diff)
lines->Expand((nLines+diff)*2);
if(diff > 0) {
for(i= nLines-1; i > next; i--)
lines->AtPut(i+diff, lines->At(i));
for(i= next+1; i <= next+diff; i++)
lines->AtPut(i, 0);
} else if (diff < 0) {
for(i= next+diff+1; i <= next; i++) {
LineMark *lm= MarkAtLine(i);
SafeDelete(lm);
}
for(i= next+1; i < nLines; i++)
lines->AtPut(i+diff, MarkAtLine(i));
for(i= nLines+diff; i < nLines; i++)
lines->AtPut(i, 0);
}
for(i= prev; i < prev+n; i++) {
LineMark *m1= MarkAtLine(i);
if(m1 == 0)
m1= new LineMark();
m1->Copy(MarkAt(newLines, i-prev));
lines->AtPut(i, m1);
}
nLines+= diff;
}
void StaticTextView::DrawLine(Point p, int i, Rectangle lr, Rectangle cr)
{
int start= StartLine(i), end= EndLine(i);
painter->Draw(text, p, start, end, lr, cr, TestFlag(eTextViewInvis));
}
void StaticTextView::SetFont(Font* fd)
{
text->SetFont(fd);
RepairAll();
}
void StaticTextView::SetFormatter(TextFormatter *f)
{
SafeDelete(formatter);
formatter= f;
}
void StaticTextView::SetPainter(TextPainter *p)
{
SafeDelete(painter);
painter= p;
}
void StaticTextView::SetPager(TextPager *p)
{
SafeDelete(pager);
pager= p;
}
void StaticTextView::SetAlign(TViewAlign m)
{
align= m;
RepairAll();
}
void StaticTextView::ShowInvisibles(bool m)
{
if (TestFlag(eTextViewInvis) != m) {
SetFlag(eTextViewInvis, m);
ForceRedraw();
}
}
bool StaticTextView::GetShowInvis()
{
return TestFlag(eTextViewInvis);
}
void StaticTextView::SetWordWrap(bool m)
{
if (wrap == m)
return;
wrap= m;
SafeDelete(formatter);
if (wrap)
formatter= new FoldingFormatter();
else
formatter= new SimpleFormatter();
RepairAll();
}
bool StaticTextView::GetWordWrap()
{
return wrap;
}
void StaticTextView::SetNoBatch(bool m)
{
SetFlag(eTextViewNoBatch, m);
}
bool StaticTextView::GetNoBatch()
{
return TestFlag(eTextViewNoBatch);
}
Text *StaticTextView::SetText(Text *t, bool scroll)
{
Text *old= text;
text= t;
SetFlag(eTextViewModified);
RepairAll();
if (scroll == cRevealTop)
Scroll(cPartScrollAbs, gPoint0, FALSE);
Send(GetId(), cPartReplacedText, 0);
return old;
}
void StaticTextView::SetString(byte *str, int len)
{
text->ReplaceWithStr(str, len);
RepairAll();
Send(GetId(), cPartReplacedText, 0);
}
void StaticTextView::RepairAll()
{
// flush line cache
LineToPoint(0);
nLines= 1;
contentRect.extent.y= 2*border.y;
LineMark *m= MarkAtLine(0);
if (m == 0) {
m= new LineMark();
lines->AtPut(0, m);
}
m->ChangeMark(0, text->Size(), LineDesc(), eStateChanged);
Repair(0, text->Size(), TRUE);
ForceRedraw();
}
void StaticTextView::SetExtent(Point p)
{
if (p != contentRect.extent) {
View::SetExtent(p);
horExtend= (p.x == cFit);
if (horExtend)
wrap= FALSE;
SafeDelete(formatter);
if (!wrap)
formatter= new SimpleFormatter();
else
formatter= new FoldingFormatter();
RepairAll();
}
}
Metric StaticTextView::GetMinSize()
{
if (TestFlag(eTextViewModified))
RepairAll();
return Metric(GetExtent(), LineToPoint(0, TRUE).y+border.y);
}
void StaticTextView::UpdateExtent(int dy)
{
Point newExtent(contentRect.extent);
Point newOrigin(contentRect.origin);
register int i, s, e;
newExtent.y+= dy;
if (horExtend) {
newExtent.x= 0;
for (i= 0; i < nLines; i++) {
s= StartLine(i);
e= EndLine(i);
newExtent.x= Math::Max(newExtent.x, painter->LineWidth(text, s, e));
}
newExtent.x += 2*border.x;
if (align != eTViewLeft)
newOrigin.x= AlignView(newExtent.x);
}
InvalidateDiff(newOrigin, newExtent);
if (newOrigin != contentRect.origin)
View::SetOrigin(newOrigin);
if (newExtent != contentRect.extent)
View::SetExtent(newExtent);
}
void StaticTextView::InvalidateDiff(Point newOrigin, Point newExtent)
{
if (newExtent != contentRect.extent) {
Rectangle r[4];
Rectangle oldRect(contentRect);
oldRect.Expand(Point(4, 0));
Rectangle newRect(newOrigin, newExtent);
newRect.Expand(Point(4, 0));
int n= Difference(r, newRect, oldRect);
for (int i= 0; i < n; i++)
InvalidateRect(r[i]);
}
}
Point StaticTextView::LineToPoint(int n, bool basePoint, bool relative)
{
register i, y= cYPosition;
// int _cYPos, _cline;
// _cYPos= cYPosition; _cline= cline;
n= Math::Range(0, nLines, n);
if (cline < n)
for(i= cline; i < n; i++)
y+= LineHeight(i);
else if (cline > n)
for(i= cline-1; i >= n; i--)
y-= LineHeight(i);
cYPosition= y;
cline= n;
// int yy= 0;
// for (int ii= 0; ii < n; ii++)
// yy+= LineHeight(ii);
// if (cYPosition != yy)
// fprintf(stderr, "LineToPoint: cYPosition %d!= %d, p=%d l=%d\n",
// cline, n, _cYPos, _cline);
if (basePoint)
y+= BaseHeight(n);
if (relative)
return Point(0, y);
return GetInnerOrigin() + Point(0, y);
}
int StaticTextView::PointToLine(Point p) // p is in coordinates relative to contentRect
{
int i, py= Math::Range(0, contentRect.Height(), p.y), lh;
// int _py, _cYPos, _cline;
// _py= py; _cYPos= cYPosition; _cline= cline;
if(py >= cYPosition) {
for(i= cline; i < nLines; i++) {
lh= LineHeight(i);
if (cYPosition + lh > py)
break;
cYPosition+= lh;
}
cline= i;
} else {
for(i= cline-1; i >= 0 ; i--) {
cYPosition-= LineHeight(i);
if (cYPosition <= py)
break;
}
cline= Math::Max(i, 0);
}
// register int ll, y2= 0;
// for (ll= 0; ll < nLines; ll++) {
// lh= LineHeight(ll);
// if (y2 + lh > py)
// break;
// y2+= lh;
// }
// if (ll != cline)
// fprintf(stderr, "PointToLine: cline %d!= %d, p=%d y=%d l=%d\n",
// cline, ll, _py, _cYPos, _cline);
return cline;
}
//---- map a point in view coordinates to line and character number
void StaticTextView::PointToPoint(
Point p, Point *viewPos, int *lineNo, int *charNo, bool relative
)
{
int start, end, cx= 0, l;
if (!relative)
p-= GetInnerOrigin();
l= PointToLine(p);
if (l >= nLines) {
l= nLines-1;
p.x= contentRect.extent.x; // set to end of line
}
if (l < 0) {
l= 0;
p.x= contentRect.origin.x; // set to start of line
}
start= StartLine(l);
end= EndLine(l);
*charNo= painter->Map(text, start, end, end, p.x, GetInnerExtent().x, &cx);
*lineNo= l;
*viewPos= Point(cx, LineToPoint(l).y);
if (!relative)
(*viewPos) += GetInnerOrigin();
}
int StaticTextView::CharToLine(int ch)
{
register int base, pos, last, s;
base= pos= 0;
last= nLines-1;
while (last >= base) { // binary search
pos= (base+last) / 2;
if (EndLine(pos) > ch && (s= StartLine(pos)) <= ch)
break;
if (StartLine(pos) > ch)
last= pos-1;
else
base= pos+1;
}
return Math::Max(0, Math::Min(pos, nLines-1));
}
int StaticTextView::CharToPoint(int charNo, int *lineNo, Point *viewPos, bool relative)
{
int line, ch, start, end, x;
Point p;
ch= Math::Range(0, text->Size(), charNo);
line= CharToLine(ch);
p.y= LineToPoint(line).y;
if (line >= nLines && line > 0) { // beyound end of text
p.y-= LineHeight(line);
line= Math::Max(0,nLines-1);
}
start= StartLine(line);
end= EndLine(line);
ch= painter->Map(text, start, end, charNo, cMaxInt, GetInnerExtent().x, &x);
*viewPos= p;
viewPos->x+= x;
if (!relative)
*viewPos += GetInnerOrigin();
*lineNo= line;
return ch;
}
int StaticTextView::LineHeight(int l)
{
l= Math::Range(0, nLines-1, l);
return MarkAtLine(l)->Height();
}
int StaticTextView::BaseHeight(int l)
{
l= Math::Range(0, nLines-1, l);
return MarkAtLine(l)->Base();
}
void StaticTextView::InvalidateRange(int from, int to)
{
if (from > to) // normalize range
SwapRange(from, to);
from= Math::Range(0, nLines-1, from);
to= Math::Range(0, nLines-1, to);
Point p= LineToPoint(from),
t= Point(contentRect.extent.x, LineToPoint(to).y+LineHeight(to));
Rectangle r= NormRect(p, t);
r += GetInnerOrigin();
if (from == 0) { // consider border
r.origin.y -= border.y;
r.extent.y += border.y;
}
if (to == nLines-1 || to == 0)
r.extent.y += border.y;
InvalidateRect(r.Expand(Point(Math::Max(4,border.x),0)));
}
int StaticTextView::AlignView(int newX)
{
int x;
// adjust origin
switch (align) {
case eTViewCenter:
x= contentRect.origin.x +
(contentRect.extent.x-newX)/2;
break;
case eTViewRight:
x= contentRect.origin.x +
(contentRect.extent.x-newX);
break;
}
return x;
}
void StaticTextView::InvalidateRange(int from, Point fp, int to, Point tp)
{
Rectangle r;
from= Math::Range(0, nLines-1, from);
to= Math::Range(0, nLines-1, to);
if ((from == to && fp.x > tp.x) || from > to) { // normalize range
SwapRange(from, to);
Swap(fp, tp);
}
if (from == to) { // optimize invalidate on one line
r= Rectangle(LineToPoint(from)+Point (fp.x,0),
Point(tp.x-fp.x, LineHeight(from)));
if (from == 0) { // consider border
r.origin.y -= border.y;
r.extent.y += border.y;
}
if (to == nLines-1 || to == 0)
r.extent.y += border.y;
r.origin+= GetInnerOrigin();
InvalidateRect(r.Expand(Point(Math::Max(4,border.x),0)));
return;
}
InvalidateRange(from, to);
}
char *StaticTextView::AsString()
{
return text->AsString();
}
OStream& StaticTextView::PrintOn (OStream&s)
{
View::PrintOn(s);
return s << text SP << wrap SP << align SP
<< horExtend SP << border SP << contentRect.extent;
}
IStream& StaticTextView::ReadFrom(IStream &s)
{
Text *txt;
bool wrap, hExtend;
Point extent, itsBorder;
TViewAlign align;
Rectangle r;
SafeDelete(text);
View::ReadFrom(s);
s >> txt >> Bool(wrap) >> Enum(align)
>> Bool(hExtend) >> itsBorder >> extent;
r= Rectangle(extent);
if (hExtend)
r.extent.x= cFit;
Init(r, txt, wrap, eTextViewNone, itsBorder, align);
return s;
}
void StaticTextView::AddMark(Mark *m)
{
text->AddMark(m);
}
Mark *StaticTextView::RemoveMark(Mark *m)
{
return text->RemoveMark(m);
}
Iterator *StaticTextView::GetMarkIter()
{
return text->GetMarkIter();
}
MarkList *StaticTextView::GetMarkList()
{
return text->GetMarkList();
}
void StaticTextView::InspectorId(char *buf, int sz)
{
if (text)
text->InspectorId(buf, sz);
else
View::InspectorId(buf, sz);
}
void StaticTextView::CollectParts(Collection* col)
{
View::CollectParts(col);
col->Add(text);
}
void StaticTextView::PrintAdorn(Rectangle, int gPageNo)
{
Rectangle r(gPrintManager->GetViewRect());
Point o;
byte *p;
Font *f= new_Font(eFontTimes, 12, eFaceBold);
r.origin.y-= 10;
p= (byte*) GetDocument()->GetBaseName();
o= f->AdjustString(p, r.NW(), eAdjVBottom, eAdjHLeft);
GrShowString(f, gInkBlack, o, p);
p= (byte*) form("Page: %d", gPageNo);
o= f->AdjustString(p, r.NE(), eAdjVBottom, eAdjHRight);
GrShowString(f, gInkBlack, o, p);
}